package wxpay

import (
	"bgs/db"
	sqlc "bgs/db/sqlc"
	nsqpUtil "bgs/nsqp/util"
	activityService "bgs/service/activity"
	afterSaleService "bgs/service/afterSale"
	brandService "bgs/service/brand"
	ledgerService "bgs/service/ledger"
	orderService "bgs/service/order"
	paymentService "bgs/service/payment"
	"bgs/util"
	wxpMinaService "bgs/wxp/mina"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/nsqio/go-nsq"
	"go.uber.org/zap"
)

// RefundNotifyAmount 退款金额
type RefundNotifyAmount struct {
	// 订单金额
	Total uint `json:"total"`
	// 退款金额
	Refund uint `json:"refund"`
	// 用户支付金额
	PayerTotal uint `json:"payer_total,omitempty"`
	// 用户退款金额
	PayerRefund uint `json:"payer_refund,omitempty"`
}

// RefundNotification 退款变更通知
type RefundNotification struct {
	// 商户ID
	BrandID string `json:"brand_id"`
	// 服务商公众号ID
	AppID string `json:"app_id"`
	// 服务商户号
	MchID string `json:"mch_id"`
	// 二级商户公众号ID
	SubAppID string `json:"sub_app_id"`
	// 二级商户号
	SubMchID string `json:"sub_mch_id"`
	// 商户订单号
	OutTradeNo string `json:"out_trade_no"`
	// 微信支付订单号
	TransactionID string `json:"transaction_id"`
	// 商户退款单号
	OutRefundNo string `json:"out_refund_no"`
	// 微信退款单号
	RefundID string `json:"refund_id"`
	// 退款状态
	RefundStatus string `json:"refund_status"`
	// 退款成功时间
	SuccessTime string `json:"success_time"`
	// 退款入账账户
	UserReceivedAccount string `json:"user_received_account"`
	// 金额信息
	Amount RefundNotifyAmount `json:"amount"`
	// 消息时间戳
	Timestamp int64 `json:"timestamp"`
	// 退回服务费
	ServiceFeeReturn int64 `json:"service_fee_return"`
}

// RefundedHandler wxpay refund message handler
type RefundedHandler struct {
	dao              *db.PgDao
	slog             *zap.SugaredLogger
	topic            string
	failover         *nsqpUtil.NsqFailover
	activityService  *activityService.ActivityServiceImp
	afterSaleService *afterSaleService.AfterSaleServiceImp
	brandService     *brandService.BrandServiceImp
	ledgerService    *ledgerService.LedgerServiceImp
	orderService     *orderService.OrderServiceImp
	paymentService   *paymentService.PaymentServiceImp
	wxpMinaService   *wxpMinaService.MinaServiceImp
}

// NewRefundedHandler create a new handler for refund
func NewRefundedHandler(
	pgdao *db.PgDao,
	slog *zap.SugaredLogger,
	topic string,
	failover *nsqpUtil.NsqFailover,
	activityService *activityService.ActivityServiceImp,
	afterSaleService *afterSaleService.AfterSaleServiceImp,
	brandService *brandService.BrandServiceImp,
	ledgerService *ledgerService.LedgerServiceImp,
	orderService *orderService.OrderServiceImp,
	paymentService *paymentService.PaymentServiceImp,
	wxpMinaService *wxpMinaService.MinaServiceImp,
) *RefundedHandler {
	return &RefundedHandler{
		dao:              pgdao,
		slog:             slog.With("nsq_consumer_handler", "RefundedHandler"),
		topic:            topic,
		failover:         failover,
		activityService:  activityService,
		afterSaleService: afterSaleService,
		brandService:     brandService,
		ledgerService:    ledgerService,
		orderService:     orderService,
		paymentService:   paymentService,
		wxpMinaService:   wxpMinaService,
	}
}

// HandleMessage implements the Handler interface.
func (h *RefundedHandler) HandleMessage(m *nsq.Message) error {
	if len(m.Body) == 0 {
		// Returning nil will automatically send a FIN command to NSQ to mark the message as processed.
		return nil
	}

	h.slog.Infof("message=>%s", string(m.Body))

	v := &RefundNotification{}
	if decodeErr := json.NewDecoder(bytes.NewReader(m.Body)).Decode(v); decodeErr != nil {
		return fmt.Errorf("无法解析消息[RefundNotification]: %s", decodeErr.Error())
	}

	reqID := strconv.FormatInt(v.Timestamp, 10)
	slog := h.slog.With("reqID", reqID)
	isSkip := v.RefundStatus == "PROCESSING"

	if !isSkip {
		isSuccess := v.RefundStatus == "SUCCESS"
		var needSendNotify bool
		// 将来抽象到对应的业务组件
		ctx := context.WithValue(context.Background(), util.RequestID{}, reqID)

		var err error
		defer h.failover.BackupSubMsgWhenHandleFail(ctx, &err, nsqpUtil.BackupSubMsgParam{
			Ts:    v.Timestamp,
			Topic: h.topic,
			Mbody: m.Body,
		})

		// orderSerialNumber := v.OutTradeNo
		afterSaleSerialNumber := v.OutRefundNo
		// refundAmount := int64(v.Amount.Refund)
		refunded, err := util.StringfToGoTime(v.SuccessTime, util.LayoutSecondWithTimeZoneAndT)
		if err != nil {
			return err
		}
		var afterSale sqlc.AfterSale
		var order sqlc.Order
		err = h.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {
			if isSuccess {
				afterSale, err = q.GetAfterSaleBySerialNumber(ctx, afterSaleSerialNumber)
				if err != nil {
					return
				}
				slog.Infof("a、refundedHandler-获取售后单 ok")
				afterSaleID := afterSale.ID
				order, err = q.GetOrder(ctx, afterSale.OrderID)
				if err != nil {
					return
				}
				slog.Infof("b、refundedHandler-获取订单 ok")

				var refundPlatformCommission uint64
				var refundDistributorCommission uint64
				var refundBonusAmount uint64
				if afterSale.IsWholeRefund {
					// 整单退
					refundPlatformCommission = uint64(order.PlatformCommissionAmount)
					refundDistributorCommission = uint64(order.DistributorCommission)
					refundBonusAmount = uint64(order.BonusAmount)
				} else {
					afterSaleDetailPtrs, err := h.afterSaleService.ListAfterSaleDetailPtrsByAfterSaleID(ctx, q, afterSaleID)
					if err != nil {
						return err
					}
					slog.Infof("c1、refundedHandler-获取售后单明细 ok")

					calculateRefundParam := &orderService.CalculateRefundParam{
						AfterSalePtr:        &afterSale,
						AfterSaleDetailPtrs: afterSaleDetailPtrs,
					}
					// 计算本次要退的平台佣金
					refundPlatformCommission, err = h.orderService.CalculateRefundPlatformCommission(ctx, q, calculateRefundParam)
					if err != nil {
						return err
					}
					slog.Infof("c2a、refundedHandler-计算本次要退的平台佣金 ok")

					// 计算本次要退的订单的分销佣金
					refundDistributorCommission, err = h.orderService.CalculateRefundDistributorCommission(ctx, q, calculateRefundParam)
					if err != nil {
						return err
					}
					slog.Infof("c2b、refundedHandler-计算本次要退的分销订单佣金 ok")

					// 计算本次要退的推广订单奖励金(计算返回为正)
					refundBonusAmount, err = h.orderService.CalculateRefundBonus(ctx, q, calculateRefundParam)
					if err != nil {
						return err
					}
					slog.Infof("c3、refundedHandler-计算本次要退的推广订单奖励金 ok")
				}

				afterSale, err = h.paymentService.DoAfterRefundSuccess(ctx, q, paymentService.AfterRefundSuccessParam{
					AfterSaleID:                 afterSale.ID,
					Refunded:                    refunded,
					RefundDistributorCommission: refundDistributorCommission,
					RefundBonusAmount:           refundBonusAmount,
					RefundWxpayCommissionAmount: uint64(v.ServiceFeeReturn),
					RefundPlatformCommission:    refundPlatformCommission,
				})
				if err != nil {
					return err
				}
				slog.Infof("d refundedHandler-paymentService.DoAfterRefundSuccess ok")
				order, err = h.orderService.DoAfterSaleDone(ctx, q, orderService.DoAfterSaleDoneParam{
					AfterSalePtr: &afterSale,
				})
				if err != nil {
					return err
				}
				slog.Infof("e c、refundedHandler-orderService.DoAfterSaleDone ok")

				if h.orderService.NeedProfitShare(&order) {
					// 是分账订单
					slog.Infof("e d、refundedHandler-(%s) is profitshare order ,need process profit return", order.SerialNumber)

					var isProfited bool
					isProfited, err = h.ledgerService.DoAfterAfterSaleRefundedSuccess(ctx, q, &order, &afterSale)
					if err != nil {
						return err
					}
					if isProfited {
						err = h.orderService.MarkOrderProfited(ctx, q, afterSale.OrderID)
						if err != nil {
							return
						}
						slog.Infof("e e refundedHandler-标记订单已分账(分账完成或者关闭) ok")
					}

					slog.Infof("e f、refundedHandler-ledgerService.DoAfterAfterSaleRefundedSuccess ok")
				}

				err = h.brandService.LoggingAccountForOrderRefund(ctx, q, &afterSale)
				if err != nil {
					return
				}
				slog.Infof("f refundedHandler-售后单退款成功后记账成功 ok")

				_, err = h.afterSaleService.MarkAccounted(ctx, q, afterSale.ID)
				if err != nil {
					return
				}
				slog.Infof("g refundedHandler-标记售后单退款已记账 ok")

				err = h.activityService.UpdateActivityOrderStatSinceAfterSaleFinished(ctx, q, activityService.UpdateActivityOrderStatSinceOrderRefundedParam{
					OrderPtr:     &order,
					AfterSalePtr: &afterSale,
				})
				if err != nil {
					return
				}
				slog.Infof("h refundedHandler-退款成功的售后单统计到(ActivityOrderStat) ok")

				err = h.activityService.UpdateActivityUserOrderStatSinceAfterSaleFinished(ctx, q, activityService.UpdateActivityUserOrderStatSinceAfterSaleFinishedParam{
					OrderPtr:     &order,
					AfterSalePtr: &afterSale,
				})
				if err != nil {
					return
				}
				slog.Infof("h refundedHandler-退款成功的售后单统计到(ActivityUserOrderStat) ok")

				afterSale, err = h.afterSaleService.MarkCounted(ctx, q, afterSale.ID)
				if err != nil {
					return
				}
				slog.Infof("i refundedHandler-标记售后单已被统计 ok")

				needSendNotify = true
			} else {
				err = h.afterSaleService.CloseAfterSaleForRefundFail(ctx, q, afterSaleSerialNumber)
				if err != nil {
					return
				}
				slog.Infof("a、refundedHandler-关闭售后单[%s] ok", v.RefundStatus)
			}
			return
		})
		if err != nil {
			return err
		}

		if needSendNotify {
			notifyParam, genErr := h.afterSaleService.GenAfterSaleStatusChangedNotifyMessage(ctx, h.dao.Q, afterSaleSerialNumber)
			if genErr != nil {
				slog.Errorf("refundedHandler 发送售后状态变更消息失败:%s", genErr.Error())
				// 不影响主流程因此不赋值给err
			}
			slog.Infof("refundedHandler notifiyParam:%#v", notifyParam)
			sendErr := h.wxpMinaService.SendNotify(ctx, &notifyParam)
			if sendErr != nil {
				slog.Errorf("refundedHandler 发送mina售后状态变更提醒失败:%s", sendErr.Error())
			}
		}

		slog.Infof("消息[RefundNotification]处理成功")
	} else {
		slog.Infof("消息[RefundNotification]忽略成功")
	}
	// Returning a non-nil error will automatically send a REQ command to NSQ to re-queue the message.
	return nil
}
